﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FreeImageAPI;
using OpenTK;
using OptixDotNet;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Scene;
using RayDen.Optix.Engine.Components;

namespace RayDen.Optix.Engine.Managers
{
    public class TexManager : OptixComponent, IDisposable
    {
        public static int MaxTextureWidth = 1024;
        public static int MaxTextureHeight = 1024;

        private readonly Dictionary<string, TextureSampler> samplers;
        private string[] textureFindDirectories;

        public TextureSampler this[string name]
        {
            get
            {
                if (!samplers.ContainsKey(name))
                    return null;
                return samplers[name];
            }
        }

        public TexManager(Context ctx, string[] texDirs)
            : base(ctx)
        {
            samplers = new Dictionary<string, TextureSampler>();
            this.textureFindDirectories = texDirs;
            this.DefaultTexture = this.CreateTextureSampler(@"F:\3D\HDRi\empty.jpg");
        }

        public string LoadTexture(string fullPath)
        {
            var fileName = Path.GetFileName(fullPath);
            if (string.IsNullOrWhiteSpace(fileName))
                return null;
            var fn = fileName.ToLower();

            if (samplers.ContainsKey(fn))
            {
                return fn;
            }

            var path = FindTexture(fullPath, textureFindDirectories);
            if (string.IsNullOrWhiteSpace(path))
                return null;

            this.CreateTextureSampler(path);

            return fn;
        }

        public void Dispose()
        {
            foreach (var textureSampler in samplers)
            {
                textureSampler.Value.Dispose();
            }
        }

        public string FindTexture(string filePath, string[] texturesDir)
        {
            if (File.Exists(filePath))
                return filePath;

            var Name = Path.GetFileName(filePath);
            if (File.Exists(Name))
            {
                return Directory.GetCurrentDirectory() + @"\" + Name;
            }
            return (from textureDir in texturesDir.Select(item => item.Trim('\r', '\n', ' '))
                where File.Exists(textureDir + "\\" + filePath)
                select textureDir + "\\" + filePath).FirstOrDefault();
        }

        public TextureSampler CreateTextureSampler(string path, bool scale = false)
        {
            var newTex = Path.GetFileName(path) ?? path;
            if (samplers.ContainsKey(newTex))
                return samplers[newTex];
            else
            {
                if (!File.Exists(path))
                {
                    Console.WriteLine("ISSUES DUDE {0} not found", path);
                    throw new ArgumentException("imagepath");
                }
                Console.WriteLine("{0} Loading. {1} total samplers loaded ", Path.GetFileName(path), samplers.Count);
                using (var bitmap = new FreeImageBitmap(path))
                {
                    var bmp = bitmap.Width > MaxTextureWidth || bitmap.Height > MaxTextureHeight
                        ? GetScaledBmp(bitmap)
                        : bitmap;
                    var desc = new BufferDesc()
                    {
                        Width = (uint) bmp.Width,
                        Height = (uint) bmp.Height,
                        Format = Format.Float4,
                        Type = BufferType.Input
                    };
                    var texData = EngineContext.Instance.ResourceManager.CreateBuffer(context, desc);

                    BufferStream data = texData.Map();
                    for (int i = 0; i < bmp.Height; i++)
                    {

                        switch (bmp.ColorDepth)
                        {

                            case 8:
                                Scanline<byte> s_8 = bmp.GetScanline<byte>(i);

                                for (int j = 0; j < bmp.Width; j++)
                                {
                                    var c = new Vector4(bmp.Palette[s_8[j]].rgbRed/255.0f,
                                        bmp.Palette[s_8[j]].rgbGreen/255.0f,
                                        bmp.Palette[s_8[j]].rgbBlue/255.0f,
                                        1.0f);
                                    data.Write(c);

                                }
                                break;

                            case 24:
                                Scanline<RGBTRIPLE> s_t = bmp.GetScanline<RGBTRIPLE>(i);
                                for (int j = 0; j < bmp.Width; j++)
                                {
                                    var rgb =
                                        new RgbSpectrum(s_t[j].rgbtRed/255.0f, s_t[j].rgbtGreen/255.0f,
                                            s_t[j].rgbtBlue/255.0f);
                                    rgb.DeGamma();
                                    var c = new Vector4(rgb.c1, rgb.c2, rgb.c3, 1.0f);
                                    data.Write(c);

                                }
                                break;

                            case 32:
                                Scanline<RGBQUAD> s_32 = bmp.GetScanline<RGBQUAD>(i);
                                for (int j = 0; j < bmp.Width; j++)
                                {
                                    var rgb =
                                        new RgbSpectrum(s_32[j].rgbRed / 255.0f,
                                                        s_32[j].rgbGreen / 255.0f,
                                                        s_32[j].rgbBlue / 255.0f);
                                    rgb.DeGamma();
                                    var c = new Vector4(rgb.c1, rgb.c2, rgb.c3, 1.0f);

                                    data.Write(c);

                                }
                                break;


                        }


                    }

                    texData.Unmap();

                    TextureSamplerDesc texDesc = TextureSamplerDesc.Default;
                    texDesc.Read = TextureReadMode.ElementType;
                    TextureSampler sampler = new TextureSampler(context, texDesc);
                    sampler.SetBuffer(texData, 0);
                    samplers.Add(newTex, sampler);
                    Console.WriteLine("2D Sampler loaded  {0} {1}x{2} pixels", path, bmp.Width, bmp.Height);
                    return sampler;
                }
            }
        }

        private static FreeImageBitmap GetScaledBmp(FreeImageBitmap bitmap)
        {
            var bmp = bitmap;
            if (bitmap.Width > MaxTextureWidth || bitmap.Height > MaxTextureHeight)
            {
                bmp = bitmap.GetScaledInstance(Math.Min(MaxTextureWidth, bitmap.Width),
                    Math.Min(MaxTextureHeight, bitmap.Height),
                    FREE_IMAGE_FILTER.FILTER_LANCZOS3);
            }
            return bmp;
        }

        public TextureSampler DefaultTexture { get; set; }
    
}
}
